# -*- coding: utf-8 -*-
"""
数据库配置模块
实现MongoDB数据库连接管理

主要功能:
- MongoDB连接配置
- 数据库连接池管理
- 数据库健康检查
- 连接重试机制
"""

import os
import asyncio
from typing import Optional, Dict, Any
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
from pymongo.errors import ConnectionFailure, ServerSelectionTimeoutError
import logging
from datetime import datetime, timezone

# 配置日志
logger = logging.getLogger(__name__)


class DatabaseConfig:
    """数据库配置类"""
    
    def __init__(self):
        # 从环境变量获取配置
        self.mongodb_url = os.getenv('MONGODB_URL', 'mongodb://localhost:27017')
        self.database_name = os.getenv('DATABASE_NAME', 'ai_marketiq')
        self.max_pool_size = int(os.getenv('MONGODB_MAX_POOL_SIZE', '100'))
        self.min_pool_size = int(os.getenv('MONGODB_MIN_POOL_SIZE', '10'))
        self.max_idle_time_ms = int(os.getenv('MONGODB_MAX_IDLE_TIME_MS', '30000'))
        self.server_selection_timeout_ms = int(os.getenv('MONGODB_SERVER_SELECTION_TIMEOUT_MS', '5000'))
        self.connect_timeout_ms = int(os.getenv('MONGODB_CONNECT_TIMEOUT_MS', '10000'))
        self.socket_timeout_ms = int(os.getenv('MONGODB_SOCKET_TIMEOUT_MS', '30000'))
        
        # 连接选项
        self.connection_options = {
            'maxPoolSize': self.max_pool_size,
            'minPoolSize': self.min_pool_size,
            'maxIdleTimeMS': self.max_idle_time_ms,
            'serverSelectionTimeoutMS': self.server_selection_timeout_ms,
            'connectTimeoutMS': self.connect_timeout_ms,
            'socketTimeoutMS': self.socket_timeout_ms,
            'retryWrites': True,
            'retryReads': True,
            'w': 'majority',
            'readPreference': 'primary',
            'readConcern': {'level': 'majority'}
        }
        
        # 连接实例
        self._client: Optional[AsyncIOMotorClient] = None
        self._database: Optional[AsyncIOMotorDatabase] = None
        self._is_connected = False
    
    async def connect(self) -> bool:
        """
        连接到MongoDB数据库
        
        Returns:
            bool: 连接是否成功
        """
        try:
            if self._client is None:
                logger.info(f"正在连接到MongoDB: {self.mongodb_url}")
                
                # 创建客户端
                self._client = AsyncIOMotorClient(
                    self.mongodb_url,
                    **self.connection_options
                )
                
                # 获取数据库实例
                self._database = self._client[self.database_name]
                
                # 测试连接
                await self._client.admin.command('ping')
                
                self._is_connected = True
                logger.info(f"成功连接到MongoDB数据库: {self.database_name}")
                
                # 创建索引
                await self._create_indexes()
                
                return True
                
        except (ConnectionFailure, ServerSelectionTimeoutError) as e:
            logger.error(f"连接MongoDB失败: {e}")
            self._is_connected = False
            return False
        except Exception as e:
            logger.error(f"连接MongoDB时发生未知错误: {e}")
            self._is_connected = False
            return False
    
    async def disconnect(self):
        """
        断开数据库连接
        """
        if self._client:
            logger.info("正在断开MongoDB连接")
            self._client.close()
            self._client = None
            self._database = None
            self._is_connected = False
            logger.info("已断开MongoDB连接")
    
    async def health_check(self) -> Dict[str, Any]:
        """
        数据库健康检查
        
        Returns:
            Dict: 健康检查结果
        """
        health_info = {
            'status': 'unhealthy',
            'database': self.database_name,
            'timestamp': datetime.now(timezone.utc).isoformat(),
            'details': {}
        }
        
        try:
            if not self._client or not self._is_connected:
                health_info['details']['error'] = '数据库未连接'
                return health_info
            
            # 执行ping命令
            start_time = asyncio.get_event_loop().time()
            await self._client.admin.command('ping')
            ping_time = (asyncio.get_event_loop().time() - start_time) * 1000
            
            # 获取服务器状态
            server_status = await self._client.admin.command('serverStatus')
            
            # 获取数据库统计
            db_stats = await self._database.command('dbStats')
            
            health_info.update({
                'status': 'healthy',
                'details': {
                    'ping_time_ms': round(ping_time, 2),
                    'server_version': server_status.get('version'),
                    'uptime_seconds': server_status.get('uptime'),
                    'connections': server_status.get('connections', {}),
                    'database_size_mb': round(db_stats.get('dataSize', 0) / 1024 / 1024, 2),
                    'collections_count': db_stats.get('collections', 0),
                    'indexes_count': db_stats.get('indexes', 0)
                }
            })
            
        except Exception as e:
            health_info['details']['error'] = str(e)
            logger.error(f"数据库健康检查失败: {e}")
        
        return health_info
    
    async def _create_indexes(self):
        """
        创建数据库索引
        """
        try:
            logger.info("开始创建数据库索引")
            
            # 这里可以添加全局索引创建逻辑
            # 具体的集合索引在各自的DAO类中创建
            
            logger.info("数据库索引创建完成")
            
        except Exception as e:
            logger.error(f"创建数据库索引失败: {e}")
    
    @property
    def client(self) -> Optional[AsyncIOMotorClient]:
        """获取MongoDB客户端"""
        return self._client
    
    @property
    def database(self) -> Optional[AsyncIOMotorDatabase]:
        """获取数据库实例"""
        return self._database
    
    @property
    def is_connected(self) -> bool:
        """检查是否已连接"""
        return self._is_connected
    
    def get_collection(self, collection_name: str):
        """
        获取集合实例
        
        Args:
            collection_name: 集合名称
            
        Returns:
            Collection: 集合实例
        """
        if not self._database:
            raise RuntimeError("数据库未连接")
        
        return self._database[collection_name]


# 全局数据库配置实例
db_config = DatabaseConfig()


async def init_database() -> bool:
    """
    初始化数据库连接
    
    Returns:
        bool: 初始化是否成功
    """
    return await db_config.connect()


async def close_database():
    """
    关闭数据库连接
    """
    await db_config.disconnect()


def get_database() -> AsyncIOMotorDatabase:
    """
    获取数据库实例
    
    Returns:
        AsyncIOMotorDatabase: 数据库实例
        
    Raises:
        RuntimeError: 数据库未连接
    """
    if not db_config.database:
        raise RuntimeError("数据库未连接，请先调用 init_database()")
    
    return db_config.database


def get_collection(collection_name: str):
    """
    获取集合实例
    
    Args:
        collection_name: 集合名称
        
    Returns:
        Collection: 集合实例
        
    Raises:
        RuntimeError: 数据库未连接
    """
    return db_config.get_collection(collection_name)


async def check_database_health() -> Dict[str, Any]:
    """
    检查数据库健康状态
    
    Returns:
        Dict: 健康检查结果
    """
    return await db_config.health_check()


# 数据库连接重试装饰器
def retry_on_connection_failure(max_retries: int = 3, delay: float = 1.0):
    """
    数据库连接失败重试装饰器
    
    Args:
        max_retries: 最大重试次数
        delay: 重试延迟（秒）
    """
    def decorator(func):
        async def wrapper(*args, **kwargs):
            last_exception = None
            
            for attempt in range(max_retries + 1):
                try:
                    return await func(*args, **kwargs)
                except (ConnectionFailure, ServerSelectionTimeoutError) as e:
                    last_exception = e
                    if attempt < max_retries:
                        logger.warning(f"数据库操作失败，{delay}秒后重试 (第{attempt + 1}次): {e}")
                        await asyncio.sleep(delay)
                        # 尝试重新连接
                        if not db_config.is_connected:
                            await db_config.connect()
                    else:
                        logger.error(f"数据库操作失败，已达到最大重试次数: {e}")
                        break
                except Exception as e:
                    # 非连接相关错误直接抛出
                    raise e
            
            # 如果所有重试都失败，抛出最后一个异常
            if last_exception:
                raise last_exception
        
        return wrapper
    return decorator